﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Net;
using System.Text;
using TouchSocket.Core;
using TouchSocket.Sockets;
using OrBitDataModule.CustomEventHandler;

namespace OrBitDataModule.Adaptor
{
    public class SocketTcpClient : IDisposable
    {
        private string AdapterName;
        private string IP;
        private int Port;

        public Encoding DefaultEncoding { get; }
        private TouchSocket.Sockets.TcpClient tcpClient = null;

        /// <summary>
        /// 设备编号
        /// </summary>
        public string MachineNo { get; }

        /// <summary>
        /// 是否在线
        /// </summary>
        public bool Online { get => tcpClient.Online; }

        /// <summary>
        /// 数据接收事件
        /// </summary>
        public event EventHandler<CustomEventHandler.ReceivedDataEventArgs> OnDataReceived;
        /// <summary>
        /// 连接成功事件
        /// </summary>
        public event EventHandler<SockeClientEventArgs> OnConnected;
        /// <summary>
        /// 连接断开事件
        /// </summary>
        public event EventHandler<SockeClientEventArgs> OnDisconnected;

        public SocketTcpClient(string machineNo, string adapterName, string ip, int port, Encoding defaultEncoding)
        {
            this.MachineNo = machineNo;
            this.AdapterName = adapterName;
            this.IP = ip;
            this.Port = port;
            this.DefaultEncoding = defaultEncoding;
            CreateTCPClient();
        }

        ~SocketTcpClient()
        {
            Dispose(false);
        }

        private void CreateTCPClient()
        {
            tcpClient = new TouchSocket.Sockets.TcpClient();
            //成功连接到服务器
            tcpClient.Connected = (client, e) =>
            {
                OnConnected?.Invoke(client.MainSocket, new SockeClientEventArgs(client.IP, client.Port, this.MachineNo, string.Empty, client.MainSocket.ProtocolType));
                return EasyTask.CompletedTask;
            };
            //从服务器断开连接，当连接不成功时不会触发。
            tcpClient.Disconnected = (client, e) =>
            {
                OnDisconnected?.Invoke(client.MainSocket, new SockeClientEventArgs(client.IP, client.Port, this.MachineNo, e.Message, client.MainSocket.ProtocolType));
                return EasyTask.CompletedTask;
            };
            //数据接收
            tcpClient.Received = (client, e) =>
            {
                byte[] buffer = new byte[e.ByteBlock.Len];
                e.ByteBlock.Read(buffer, 0, e.ByteBlock.Len);
                OnDataReceived?.Invoke(client.MainSocket, new CustomEventHandler.ReceivedDataEventArgs(this, client.IP, client.Port, this.MachineNo, buffer, e.ByteBlock.Len, client.MainSocket.ProtocolType));
                return EasyTask.CompletedTask;
            };

            //声明配置
            TouchSocketConfig config = InitClientConfig(AdapterName, IP, Port);
            //载入配置
            tcpClient.Setup(config);
        }

        /// <summary>
        /// 连接
        /// </summary>
        /// <returns></returns>
        public (bool Result, string Message) Connect()
        {
            var result = tcpClient.TryConnect();
            return (result.IsSuccess(), result.Message);
        }

        /// <summary>
        /// 连接
        /// </summary>
        /// <returns></returns>
        public void DisConnect()
        {
            tcpClient.Close();
        }

        /// <summary>
        /// 给客户端发送消息
        /// </summary>
        /// <param name="message"></param>
        public void Send(string message)
        {
            if (tcpClient.Online)
            {
                tcpClient.Send(message);
            }
        }

        /// <summary>
        /// 给客户端发送消息
        /// </summary>
        /// <param name="buffer"></param>
        public void Send(byte[] buffer) => Send(buffer, 0, buffer.Length);

        /// <summary>
        /// 给客户端发送消息
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <param name="length"></param>
        public void Send(byte[] buffer, int offset, int length)
        {
            if (tcpClient.Online)
            {
                tcpClient.Send(buffer, offset, length);
            }
        }

        private TouchSocketConfig InitClientConfig(string adapterName, string ip, int port)
        {
            TouchSocketConfig config = new TouchSocketConfig();
            IPAddress localIPAddress = null;
            if (!string.IsNullOrEmpty(adapterName))
            {
                localIPAddress = GetAdapterIPAddress(adapterName);
            }
            if (localIPAddress == null)
            {
                config.SetRemoteIPHost(new IPHost($"{ip}:{port}"));
                config.ConfigureContainer(a => a.AddConsoleLogger()).ConfigurePlugins(p =>
                {
                    p.UseReconnection(-1, true, 1000);//如需永远尝试连接，tryCount设置为-1即可。
                });
            }
            else
            {
                int localPort = GetAvailablePort(100, 50000);
                config.SetRemoteIPHost(new IPHost($"{ip}:{port}"))
                    .SetBindIPHost(new TouchSocket.Sockets.IPHost(localIPAddress, localPort)); ;
                config.ConfigureContainer(a => a.AddConsoleLogger()).ConfigurePlugins(p =>
                {
                    p.UseReconnection(-1, true, 1000);//如需永远尝试连接，tryCount设置为-1即可。
                });
            }
            return config;
        }

        int GetAvailablePort(int startPort, int endPort)
        {
            for (int port = startPort; port <= endPort; port++)
            {
                if (IsPortAvailable(port))
                    return port;
            }
            return 0;
        }

        bool IsPortAvailable(int port)
        {
            var listener = new TcpListener(IPAddress.Any, port);
            try
            {
                listener.Start();
                return true;
            }
            catch
            {
                return false;
            }
            finally
            {
                listener.Stop();
            }
        }
        //根据网络适配器获取IP
        IPAddress GetAdapterIPAddress(string adapterName)
        {
            NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
            foreach (NetworkInterface adapter in adapters)
            {
                if (adapter.Name == adapterName)
                {
                    if (adapter.Supports(NetworkInterfaceComponent.IPv4))
                    {
                        IPInterfaceProperties adapterProperties = adapter.GetIPProperties();
                        foreach (UnicastIPAddressInformation ip in adapterProperties.UnicastAddresses)
                        {
                            if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                            {
                                return ip.Address;
                            }
                        }
                    }
                }
            }
            return null;
        }

        public void Dispose()
        {
            Dispose(true);
            System.GC.SuppressFinalize(this);
        }
        protected void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (tcpClient.Online)
                {
                    tcpClient.Close();
                    tcpClient.Dispose();
                }
            }
        }
    }
}
